package com.tanhua.sso.service;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.dubbo.config.annotation.Reference;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.tanhua.common.enums.LogTypeEnum;
import com.tanhua.common.mapper.QuestionUserLockMapper;
import com.tanhua.common.mapper.QuestionnaireMapper;
import com.tanhua.common.mapper.UserMapper;
import com.tanhua.common.pojo.Log;
import com.tanhua.common.pojo.QuestionUserLock;
import com.tanhua.common.pojo.Questionnaire;
import com.tanhua.common.pojo.User;
import com.tanhua.common.service.LogService;
import com.tanhua.dubbo.server.api.HuanXinApi;
import io.jsonwebtoken.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.joda.time.DateTime;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.messaging.MessagingException;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @Author 王加鑫
 * @Date 2021/09/01 21:41
 * @Since JDK 1.8
 */
@Service
@Slf4j
public class UserService {

    @Value("${jwt.secret}")
    private String secret;

    @Reference(version = "1.0.0")
    private HuanXinApi huanXinApi;

    @Autowired
    private RedisTemplate<String,String> redisTemplate;

    @Autowired
    private RocketMQTemplate rocketMQTemplate;

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private LogService logService;

    @Autowired
    private QuestionnaireMapper questionnaireMapper;

    @Autowired
    private QuestionUserLockMapper questionUserLockMapper;

    /**
     * 根据用户传输过来的token来查询该用户的信息
     * @param token
     * @return
     */
    public User queryUserByToken(String token) {
        try {
            // 通过token解析数据
            Map<String, Object> body = Jwts.parser()
                    .setSigningKey(secret)//盐值要相同
                    .parseClaimsJws(token)
                    .getBody();
            // 从token中获取数据
            User user = new User();
            user.setId(Long.valueOf(body.get("id").toString()));
            // 要获取user对象中的mobile(?作用)
            // 如果经常从数据库中查询mobile会影响性能，所以可以将其存放到redis缓存中
            String redisKey = "TANHUA_USER_MOBILE_" + user.getId();
            if(this.redisTemplate.hasKey(redisKey)){
                //如果redis缓存中存有该手机号就直接获取
                user.setMobile(this.redisTemplate.opsForValue().get(redisKey));
            }
            //如果redis缓存中没有该手机号就需要去数据库查询获取
            User u = userMapper.selectById(user.getId());
            String mobile = u.getMobile();
            user.setMobile(mobile);
            //将获取到的mobile存放到redis中,方便下次从缓存中取
            //body.get("exp") 获取token过期时间
            //需要设置redis缓存失效时间（不能超过token失效时间）
            long timeout = Long.valueOf(body.get("exp").toString()) * 1000 - System.currentTimeMillis();
            this.redisTemplate.opsForValue().set(redisKey,mobile,timeout,TimeUnit.MILLISECONDS);
            return user;
        } catch (ExpiredJwtException e) {
            System.out.println("token已经过期！");
        } catch (Exception e) {
            System.out.println("token不合法！");
        }
        return null;
    }

    /**
     * 校验验证码是否正确
     * @param verificationCode
     * @return
     */
    public String login(String phone, String verificationCode) {
        //1、首先要去redis中去获取该验证码是否存在
        String redisKey="CHECK_CODE"+phone;
        String code = this.redisTemplate.opsForValue().get(redisKey);
        //1.1、判断缓存中是否有记录（校验验证码）
        if(!StringUtils.equals(verificationCode,code)){
            //到这说明验证码输入错误
            return null;
        }
        //2、到这说明验证码输入正确，用户输入验证码过后需要将验证码废弃
        this.redisTemplate.delete(redisKey);
        //3、需要去数据库里面查询该手机号是否存在了
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("mobile",phone);
        //4、根据mp的条件查询对应手机号的user对象
        User user = userMapper.selectOne(queryWrapper);
        //标记位，默认这个用户是一个老用户
        Boolean isNew = false;
        //5、判断用户是否是新老用户，进行不同的逻辑处理
        if(null == user){
            //避免抛空指针异常
            user = new User();
            //查询不出数据说明手机号不存在,用户说明就是新用户，需要注册
            user.setMobile(phone);
            //给用户一个初始密码，顺带加个密
            user.setPassword(DigestUtils.md5Hex("123456"));
            userMapper.insert(user);
            //标记这个用户是一个新用户
            isNew = true;

            //注册环信用户
            Boolean result = this.huanXinApi.register(user.getId());
            if(!result){
                //注册环信失败，记录日志
                log.error("注册环信用户失败~ userId = " + user.getId());
            }

            //添加用户
            List<Questionnaire> questionnaireList = questionnaireMapper.selectList(null);
            List<Object> ids = CollUtil.getFieldValues(questionnaireList, "id");
            for (Object id : ids) {
                QuestionUserLock questionUserLock = new QuestionUserLock();
                questionUserLock.setUserId(user.getId());
                questionUserLock.setQuestionnaireId((Long)id);
                if ((Long)id == 1){
                    questionUserLock.setIsLock(0);
                }else {
                    questionUserLock.setIsLock(1);
                }
                questionUserLockMapper.insert(questionUserLock);
            }
        }

        // 判断用户账号是否冻结登录
        String freezeRange = redisTemplate.opsForValue().get("USER_FREEZE_RANGE_" + user.getId());
        if (ObjectUtil.isNotEmpty(freezeRange) && freezeRange.equals("1")){
            return "freeze";
        }

        //6、不管是新老用户，都需要给他生成一个token
        Map<String, Object> calims = new HashMap<>();
        // token里面携带着用户的id
        calims.put("id", user.getId());
        //7、使用JWT加密的形式生成token
        String token = Jwts.builder()
                .setClaims(calims)
                .signWith(SignatureAlgorithm.HS256,secret)
                .setExpiration(new DateTime().plusHours(12).toDate())
                .compact();

        User user1 = queryUserByToken(token);
        com.tanhua.common.pojo.Log log1 = new Log();
        log1.setId(user1.getId());
        log1.setLogTime(System.currentTimeMillis());
        log1.setType(LogTypeEnum.LOGIN);
        log1.setEquipment("iphone");
        log1.setPlace("合肥");
        logService.saveLog(log1);

        //8、发送用户登录成功的指令
        try {
            Map<String,Object> msg = new HashMap<>();
            msg.put("id", user.getId());
            msg.put("date", System.currentTimeMillis());
            rocketMQTemplate.convertAndSend("tanhua-sso-login",msg);
        } catch (MessagingException e) {
            log.error("发送消息失败！",e);
        }

        return token + "|" + isNew;

    }

}
